package hu.mapro.rollerx.core;

import hu.mapro.rollerx.core.RollerWorld.ContactMember;
import hu.mapro.rollerx.core.RollerWorld.ElapsedHandler;
import hu.mapro.rollerx.core.RollerWorld.UserData;

import java.util.ArrayList;
import java.util.Collection;

import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.Body;
import com.badlogic.gdx.physics.box2d.BodyDef;
import com.badlogic.gdx.physics.box2d.BodyDef.BodyType;
import com.badlogic.gdx.physics.box2d.Contact;
import com.badlogic.gdx.physics.box2d.Fixture;
import com.badlogic.gdx.physics.box2d.FixtureDef;
import com.badlogic.gdx.physics.box2d.PolygonShape;

public class Cargo1 implements Cargo {

	// cargo
	
	private static final float CARGO_WIDTH = 1.5f;
	private static final float CARGO_HEIGHT = 0.75f;
	//private static final float CARGO_ROTATION = 3.0f * MathUtils.PI2;
	private static final int CARGO_BREAK_WIDTH = 3;
	private static final int CARGO_BREAK_HEIGHT = 2;
	private static final float CARGO_BREAK_GAP = 0.05f;
	private static final int CARGO_COUNT = 2;
	//int cargoCount = 0;
	
	static final float CARGO_PERIOD = 2f;
	float cargoAccumulator;
	
	private static final float CARGO_BROKEN_LIFETIME = 1f;

	private BodyDef cargoBodyDef;

	private PolygonShape cargoShape;

	private UserData cargoUserData;

	private Collection<Body> fallenCargo = new ArrayList<Body>();

	private BodyDef brokenCargoBodyDef;

	private PolygonShape[] brokenCargoShapes;
	private FixtureDef cargoFixtureDef;
	private FixtureDef cargoGroundSensorFixtureDef;
	private FixtureDef brokenCargoFixtureDef;
	private RollerWorld rollerWorld;
	
	private void breakCargo(Body cargo) {
		for (PolygonShape shape : brokenCargoShapes) {
			brokenCargoBodyDef.position.set(cargo.getPosition());
			brokenCargoBodyDef.angle = cargo.getAngle();
			brokenCargoBodyDef.linearVelocity.set(cargo.getLinearVelocity());
			brokenCargoBodyDef.angularVelocity = cargo.getAngularVelocity();
			Body brokenCargoBody = rollerWorld.world.createBody(brokenCargoBodyDef);
			brokenCargoFixtureDef.shape = shape;
			brokenCargoBody.createFixture(brokenCargoFixtureDef);
			rollerWorld.elapsedHandlers.add(new BrokenCargoHandler(brokenCargoBody));
		}
		rollerWorld.world.destroyBody(cargo);
	}

	@Override
	public void init(RollerWorld rollerWorld, Vector2 carPosition, float platformHeight) {
		this.rollerWorld = rollerWorld;
		
		cargoBodyDef = new BodyDef();
		cargoBodyDef.type = BodyType.DynamicBody;
		cargoShape = new PolygonShape();
		Vector2 cargoVertices[] = new Vector2[] {
				new Vector2(-CARGO_WIDTH/2.0f, -CARGO_HEIGHT/2.0f),
				new Vector2(CARGO_WIDTH/2.0f, -CARGO_HEIGHT/2.0f),
				new Vector2(CARGO_WIDTH/2.0f, CARGO_HEIGHT/2.0f),
				new Vector2(-CARGO_WIDTH/2.0f, CARGO_HEIGHT/2.0f),
		};
		cargoShape.set(cargoVertices);
		cargoBodyDef.position.y = carPosition.y + platformHeight + CARGO_HEIGHT / 2.0f;

		cargoUserData = new CargoUserData();
		
		float brokenWidth = CARGO_WIDTH / CARGO_BREAK_WIDTH;
		float brokenHeight = CARGO_HEIGHT / CARGO_BREAK_HEIGHT;
		
		brokenCargoBodyDef = new BodyDef();
		brokenCargoBodyDef.type = BodyType.DynamicBody;
		brokenCargoShapes = new PolygonShape[CARGO_BREAK_WIDTH*CARGO_BREAK_HEIGHT];
		int shapeCount = 0;
		
		
		for (int i=0 ; i<CARGO_BREAK_WIDTH ; i++) {
			for (int j=0 ; j<CARGO_BREAK_HEIGHT ; j++) {
				Vector2 brokenCargoVertices[] = new Vector2[] {
						new Vector2(-CARGO_WIDTH/2.0f+(i+CARGO_BREAK_GAP)*brokenWidth, -CARGO_HEIGHT/2.0f+(j+CARGO_BREAK_GAP)*brokenHeight),
						new Vector2(-CARGO_WIDTH/2.0f+(i+1-CARGO_BREAK_GAP)*brokenWidth, -CARGO_HEIGHT/2.0f+(j+CARGO_BREAK_GAP)*brokenHeight),
						new Vector2(-CARGO_WIDTH/2.0f+(i+1-CARGO_BREAK_GAP)*brokenWidth, -CARGO_HEIGHT/2.0f+(j+1-CARGO_BREAK_GAP)*brokenHeight),
						new Vector2(-CARGO_WIDTH/2.0f+(i+CARGO_BREAK_GAP)*brokenWidth, -CARGO_HEIGHT/2.0f+(j+1-CARGO_BREAK_GAP)*brokenHeight)
				};
				
				PolygonShape brokenCargoShape = new PolygonShape();
				brokenCargoShape.set(brokenCargoVertices);
				
				brokenCargoShapes[shapeCount++] = brokenCargoShape;
			}
		}
		
		
		cargoFixtureDef = new FixtureDef();
		cargoFixtureDef.density = 1.0f;
		cargoFixtureDef.filter.categoryBits = RollerWorld.COLLISION_CARGO;
		cargoFixtureDef.filter.maskBits = RollerWorld.COLLISION_CARGO | RollerWorld.COLLISION_CAR;
		cargoFixtureDef.shape = cargoShape;
		
		cargoGroundSensorFixtureDef = new FixtureDef();
		cargoGroundSensorFixtureDef.isSensor = true;
		cargoGroundSensorFixtureDef.filter.categoryBits = RollerWorld.COLLISION_CARGO_GROUND_SENSOR;
		cargoGroundSensorFixtureDef.filter.maskBits = RollerWorld.COLLISION_GROUND;
		cargoGroundSensorFixtureDef.shape = cargoShape;
		
		brokenCargoFixtureDef = new FixtureDef();
		brokenCargoFixtureDef.density = 1.0f;
		brokenCargoFixtureDef.filter.categoryBits = RollerWorld.COLLISION_BROKEN_CARGO;
		brokenCargoFixtureDef.filter.maskBits = RollerWorld.COLLISION_BROKEN_CARGO | RollerWorld.COLLISION_GROUND;
		
		for (int i=0;i<CARGO_COUNT;i++) {
			cargoBodyDef.position.x = carPosition.x-(CARGO_WIDTH*CARGO_COUNT)/2.0f + CARGO_WIDTH/2.0f + i*CARGO_WIDTH;
			Body cargoBody = rollerWorld.world.createBody(cargoBodyDef);
			cargoBody.setUserData(cargoUserData);
			cargoBody.createFixture(cargoFixtureDef);
			cargoBody.createFixture(cargoGroundSensorFixtureDef);
			
		}
	}

	public void cargoHitGround(Fixture cargoFixture) {
		Body cargoBody = cargoFixture.getBody();
		
		if (cargoBody.getUserData()!=null) {
			cargoBody.setUserData(null);
			fallenCargo.add(cargoBody);
		}
	}

	private final class CargoUserData implements UserData {
		//public boolean removed = false;

		@Override
		public void contact(UserData contactedUserData, Contact contact, ContactMember contactedMember) {
			contactedUserData.contactCargo(contact, ContactMember.A);
		}

		@Override
		public void contactGround(Contact contact, ContactMember contactedMember) {
			cargoHitGround(contactedMember.otherFixture(contact));
		}

		@Override
		public void contactCargo(Contact contact, ContactMember a) {
		}

	}

	class BrokenCargoHandler extends ElapsedHandler {

		private Body brokenCargoBody;
		
		BrokenCargoHandler(Body body) {
			rollerWorld.super();
			this.brokenCargoBody = body;
		}
		
		@Override
		boolean elapsed() {
			if (age() > CARGO_BROKEN_LIFETIME) {
				rollerWorld.world.destroyBody(brokenCargoBody);
				return true;
			}
			return false;
		}

	}

	@Override
	public void step() {
		for (Body cargo : fallenCargo) {
			breakCargo(cargo);
		}
		fallenCargo.clear();
	}

	@Override
	public void dispose() {
	}
	
}
